<!DOCTYPE html>
<html lang="en">

<head>

    <title>虚拟桌面 | noVNC Viewer</title>

    <meta charset="utf-8">

    <style>
        body {
            margin: 0;
            background-color: dimgrey;
            height: 100%;
            display: flex;
            flex-direction: column;
        }

        html {
            height: 100%;
        }

        #top_bar {
            background-color: #547c72;
            color: white;
            font: bold 12px Helvetica;
            padding: 6px 5px 4px 5px;
            border-bottom: 1px outset;
        }

        #status {
            text-align: center;
        }

        #statusDesktopName {
            position: fixed;
            top: 0px;
            left: 0px;
            padding: 5px 5px 4px 5px;
        }

        #toggleFullscreen,
        #sendCtrlAltDelButton {
            position: fixed;
            top: 0px;
            border: 1px outset;
            padding: 5px 5px 4px 5px;
            cursor: pointer;
        }

        #sendCtrlAltDelButton {
            right: 0px;
        }

        #toggleFullscreen {
            right: 145px;
        }

        #screen {
            flex: 1;
            /* fill remaining space */
            overflow: hidden;
        }
    </style>

    <script src="../spice-web-client/lib/modernizr.js"></script>
    <script src="../spice-web-client/lib/jquery-2.0.3.js"></script>
    <script src="../spice-web-client/lib/jquery-mousewheel.js"></script>
    <script src="../spice-web-client/lib/jgestures.min.js"></script>
    <script src="../spice-web-client/lib/pixastic.js"></script>
    <script src="../spice-web-client/lib/base64.js"></script>
    <script src="../spice-web-client/lib/biginteger.js"></script>
    <script src="../spice-web-client/lib/virtualjoystick.js"></script>
    <script src="../spice-web-client/lib/prettyprint.js"></script>
    <script src="../spice-web-client/lib/jsbn.js"></script>
    <script src="../spice-web-client/lib/jsbn2.js"></script>
    <script src="../spice-web-client/lib/prng4.js"></script>
    <script src="../spice-web-client/lib/rng.js"></script>
    <script src="../spice-web-client/lib/sha1.js"></script>
    <script src="../spice-web-client/lib/encrypt.js"></script>
    <script src="../spice-web-client/swcanvas/swcanvas.js"></script>
    <script src="../spice-web-client/lib/bowser.js"></script>
    <script src="../spice-web-client/lib/utils.js"></script>
    <script src="../spice-web-client/lib/flipper.js"></script>
    <script src="../spice-web-client/lib/CollisionDetector.js"></script>
    <script src="../spice-web-client/lib/GlobalPool.js"></script>
    <script src="../spice-web-client/lib/GenericObjectPool.js"></script>
    <script src="../spice-web-client/lib/AsyncConsumer.js"></script>
    <script src="../spice-web-client/lib/AsyncWorker.js"></script>
    <script src="../spice-web-client/lib/PacketWorkerIdentifier.js"></script>
    <script src="../spice-web-client/spiceobjects/spiceobjects.js"></script>
    <script src="../spice-web-client/spiceobjects/generated/protocol.js"></script>
    <script src="../spice-web-client/lib/graphicdebug.js"></script>
    <script src="../spice-web-client/lib/images/lz.js"></script>
    <script src="../spice-web-client/lib/images/bitmap.js"></script>
    <script src="../spice-web-client/lib/images/png.js"></script>
    <script src="../spice-web-client/lib/runqueue.js"></script>
    <script src="../spice-web-client/lib/graphic.js"></script>
    <script src="../spice-web-client/lib/queue.js"></script>
    <script src="../spice-web-client/lib/ImageUncompressor.js"></script>
    <script src="../spice-web-client/lib/SyncAsyncHandler.js"></script>
    <script src="../spice-web-client/lib/IntegrationBenchmark.js"></script>
    <script src="../spice-web-client/lib/stuckkeyshandler.js"></script>
    <script src="../spice-web-client/lib/timelapsedetector.js"></script>
    <script src="../spice-web-client/lib/displayRouter.js"></script>
    <script src="../spice-web-client/lib/rasterEngine.js"></script>
    <script src="../spice-web-client/lib/DataLogger.js"></script>
    <script src="../spice-web-client/network/socket.js"></script>
    <script src="../spice-web-client/network/clusternodechooser.js"></script>
    <script src="../spice-web-client/network/socketqueue.js"></script>
    <script src="../spice-web-client/network/packetcontroller.js"></script>
    <script src="../spice-web-client/network/packetextractor.js"></script>
    <script src="../spice-web-client/network/packetreassembler.js"></script>
    <script src="../spice-web-client/network/reassemblerfactory.js"></script>
    <script src="../spice-web-client/network/sizedefiner.js"></script>
    <script src="../spice-web-client/network/packetlinkfactory.js"></script>
    <script src="../spice-web-client/network/spicechannel.js"></script>
    <script src="../spice-web-client/network/busconnection.js"></script>
    <script src="../spice-web-client/network/websocketwrapper.js"></script>
    <script src="../spice-web-client/network/connectioncontrol.js"></script>
    <script src="../spice-web-client/application/agent.js"></script>
    <script src="../spice-web-client/application/spiceconnection.js"></script>
    <script src="../spice-web-client/application/clientgui.js"></script>
    <script src="../spice-web-client/application/packetprocess.js"></script>
    <script src="../spice-web-client/application/packetfilter.js"></script>
    <script src="../spice-web-client/application/packetfactory.js"></script>
    <script src="../spice-web-client/application/application.js"></script>
    <script src="../spice-web-client/application/virtualmouse.js"></script>
    <script src="../spice-web-client/application/imagecache.js"></script>
    <script src="../spice-web-client/application/rasteroperation.js"></script>
    <script src="../spice-web-client/application/stream.js"></script>
    <script src="../spice-web-client/application/inputmanager.js"></script>
    <script src="../spice-web-client/process/busprocess.js"></script>
    <script src="../spice-web-client/process/displayprocess.js"></script>
    <script src="../spice-web-client/process/displaypreprocess.js"></script>
    <script src="../spice-web-client/process/inputprocess.js"></script>
    <script src="../spice-web-client/process/cursorprocess.js"></script>
    <script src="../spice-web-client/process/playbackprocess.js"></script>
    <script src="../spice-web-client/process/mainprocess.js"></script>
    <script src="../spice-web-client/keymaps/keymapes.js"></script>
    <script src="../spice-web-client/keymaps/keymapit.js"></script>
    <script src="../spice-web-client/keymaps/keymapus.js"></script>
    <script src="../spice-web-client/keymaps/keymap.js"></script>
    <script src="../spice-web-client/application/WorkerProcess.js"></script>

    <script type="module" crossorigin="anonymous">
        // RFB holds the API to connect and communicate with a VNC server
        import RFB from './src/core/rfb.js';

        let rfb;
        let desktopName;

        // When this function is called we have
        // successfully connected to a server
        function connectedToServer(e) {
            status("cecd.com")
            statusDesktopName(desktopName);
        }

        // This function is called when we are disconnected
        function disconnectedFromServer(e) {
            if (e.detail.clean) {
                status("Disconnected");
            } else {
                status("Something went wrong, connection is closed");
            }
        }

        // When this function is called, the server requires
        // credentials to authenticate
        function credentialsAreRequired(e) {
            const password = prompt("Password Required:");
            rfb.sendCredentials({ password: password });
        }

        // When this function is called we have received
        // a desktop name from the server
        function updateDesktopName(e) {
            //desktopName = e.detail.name;
        }

        // Since most operating systems will catch Ctrl+Alt+Del
        // before they get a chance to be intercepted by the browser,
        // we provide a way to emulate this key sequence.
        function sendCtrlAltDel() {
            rfb.sendCtrlAltDel();
            return false;
        }

        function toggleFullscreen() {
            let elem = document.documentElement
            if (
                !document.fullscreenElement &&
                !document.mozFullScreenElement &&
                !document.webkitFullscreenElement &&
                !document.msFullscreenElement
            ) {
                if (elem.requestFullscreen) {
                    elem.requestFullscreen();
                } else if (elem.mozRequestFullScreen) {
                    elem.mozRequestFullScreen();
                } else if (elem.webkitRequestFullscreen) {
                    elem.webkitRequestFullscreen();
                } else if (elem.msRequestFullscreen) {
                    elem.msRequestFullscreen();
                }
                isFullscreen = true;

            } else {
                if (document.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.mozCancelFullScreen) {
                    document.mozCancelFullScreen();
                } else if (document.webkitExitFullscreen) {
                    document.webkitExitFullscreen();
                } else if (document.msExitFullscreen) {
                    document.msExitFullscreen();
                }
                isFullscreen = false;
            }
        }

        // Show a status text in the top bar
        function status(text) {
            document.getElementById('status').textContent = text;
        }

        function statusDesktopName(text) {
            document.getElementById('statusDesktopName').textContent = text;
        }

        // This function extracts the value of one variable from the
        // query string. If the variable isn't defined in the URL
        // it returns the default value instead.
        function readQueryVariable(name, defaultValue) {
            // A URL with a query parameter can look like this:
            // https://www.example.com?myqueryparam=myvalue
            //
            // Note that we use location.href instead of location.search
            // because Firefox < 53 has a bug w.r.t location.search
            const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
                match = document.location.href.match(re);

            if (match) {
                // We have to decode the URL since want the cleartext value
                return decodeURIComponent(match[1]);
            }

            return defaultValue;
        }

        function getWebViewer() {
            let urlCookie = window.location.href.slice(window.location.href.indexOf('?') + 1).split("cookie=")
            if (urlCookie.length > 1) {
                const webViewer = JSON.parse(atob(decodeURIComponent(urlCookie[1])))['web_viewer']
                // Check expiration
                if (new Date() > new Date(webViewer.exp * 1000)) {
                    alert('Token expired, can\'t access the desktop')
                    return false
                } else {
                    return webViewer
                }
            }
            var match = document.cookie.match(new RegExp('(^| )browser_viewer=([^;]+)'));
            return JSON.parse(atob(decodeURIComponent(match[2])))['web_viewer'];
            if (!match) {
                return false
            }
        }

        let webViewer = getWebViewer()

        function getParameter(name) {
            return webViewer[name] ? webViewer[name] : false
        }

        function resize() {
            let app = new Application();

            let f = (action, params) => {
                if (action === 'ready') {
                    app.sendCommand('setResolution', {
                        'width': document.getElementById("screen").offsetWidth,
                        'height': document.getElementById("screen").offsetHeight
                    });
                    app.dispose();
                }
            }

            app.run({
                'callback': f,
                'protocol': 'wss',
                'host': getParameter('host') || readQueryVariable('host', window.location.hostname),
                'port': getParameter('port') || '443',
                'desktopName': decodeURI(getParameter('vmName')) || decodeURI(readQueryVariable('vmName')),
                'vmHost': getParameter('vmHost') || readQueryVariable('vmHost'),
                'vmPort': (getParameter('vmPort') || readQueryVariable('vmPort')) - 2, // the spice (no TLS) port is two ports less than the vnc
                'token': getParameter('token') || readQueryVariable('passwd'),
                'clientOffset': {
                    'x': 0,
                    'y': 0
                },
            });
        }

        document.getElementById('sendCtrlAltDelButton')
            .onclick = sendCtrlAltDel;
        document.getElementById('toggleFullscreen').onclick = toggleFullscreen;

        // Read parameters specified in the URL query string
        // By default, use the host and port of server that served this file
        desktopName = getParameter('vmName') || readQueryVariable('vmName');
        const host = getParameter('host') || readQueryVariable('host', window.location.hostname);
        const port = getParameter('port') === false || getParameter('port') === undefined ? '' : ':' + getParameter('port');
        const vmHost = getParameter('vmHost') || readQueryVariable('vmHost');
        const vmPort = getParameter('vmPort') || readQueryVariable('vmPort');
        const password = getParameter('token') || readQueryVariable('passwd');

        // | | |         | | |
        // | | | Connect | | |
        // v v v         v v v

        status("Connecting");

        resize()

        // Build the websocket URL used to connect
        let url = 'wss://' + host + port + '/' + vmHost + '/' + vmPort

        // Creating a new RFB object will start a new connection
        rfb = new RFB(document.getElementById('screen'), url,
            { credentials: { password: password } });

        // Add listeners to important events from the RFB module
        rfb.addEventListener("connect", connectedToServer);
        rfb.addEventListener("disconnect", disconnectedFromServer);
        rfb.addEventListener("credentialsrequired", credentialsAreRequired);
        rfb.addEventListener("desktopname", updateDesktopName);

        // Set parameters that can be changed on an active connection
        rfb.scaleViewport = true;

        // Resize the window
        let resizeTimeout;
        window.addEventListener('resize', (e) => {
            clearTimeout(resizeTimeout);
            resizeTimeout = setTimeout(resize, 500);
        });
    </script>
</head>

<body>
    <div id="top_bar">
        <div id="statusDesktopName"></div>
        <div id="status">Loading...</div>
        <div id="sendCtrlAltDelButton">Send 'Ctrl+Alt+Supr'</div>
        <div id="toggleFullscreen">Full Screen</div>
    </div>
    <div id="screen">
        <!-- This is where the remote screen will appear -->
    </div>
</body>

</html>